#include <iostream>
#include <string>
#include <strings.h>
#include <cstdlib>
#include <unistd.h>
#include <netinet/in.h> 
#include <cassert>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>

// 客户端k
// 需要知道 server web 的 ip && port 
// ./udpClient server_ip server_port

struct sockaddr_in server;   // 创建 sockaddr_in 关于 服务端 的结构体

// 这个线程负责处理收到的数据
// 主线程负责打印数据
void* custom_printer(void* args)
{
    int sockfd = *((int*) args);
    // 接收服务器发送回来的响应
    char recvmsg[1024];
    while (true)
    {
        // 最后两个参数是输入输出型参数
        struct sockaddr_in temp;
        socklen_t len = sizeof temp;
        // 接收服务器的回响数据，会存在 recvmsg 中
        ssize_t rd = recvfrom(sockfd, recvmsg, sizeof(recvmsg) - 1, 0, (struct sockaddr*) &temp, &len);
        if (rd > 0) 
        {
            recvmsg[rd] = '\0';
            std::cout << "server echo -> " << recvmsg << std::endl;
        }
    }
}

int main(int argc, char* argv[])
{
    if (argc != 3) {
        std::cout << "Usage: \n\t" << "udpClient server_ip server_port" << std::endl;
        exit(1);
    }
    // 1. 创建客户端
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sockfd > 0);

    // 设定要访问的服务器
    std::string svrIp = argv[1];      // 获取客户端 IP 地址
    uint16_t svrPort = atoi(argv[2]); // 获取服务端端口号

    //    客户端不需要 bind, os 负责处理 ip && port
    // 填写服务器对应的信息
    bzero(&server, sizeof server);
    server.sin_family = AF_INET; // 表示网络通信 IPv4
    server.sin_port = htons(svrPort);
    server.sin_addr.s_addr = inet_addr(svrIp.c_str());

    // 3. 开始通信
    std::string sendmsg ;
    pthread_t thr;
    pthread_create(&thr, nullptr, custom_printer, (void*) &sockfd);
    while (true)
    {
        // 这里往 cerr 中打印（也就是显示屏）
        // 然后将客户单收到的数据 回响到 stdout 中，但是再重定向到 fifo 文件中
        // 就可以实现聊天室的功能
        std::cerr << "enter your msg -> ";
        std::getline(std::cin, sendmsg);
        // 首次调用 sendto 的时候，os 会绑定客户端这边的 ip && port, 最后那两个参数表示发送给谁
        sendto(sockfd, sendmsg.c_str(), sendmsg.size(), 0, (const struct sockaddr*) &server, sizeof server);
    }
    return 0;
}